﻿using System;
using System.Net.Sockets;
using System.Net;
using System.Collections.Concurrent;

namespace Core
{
    public class TcpListenerSystem : EntitySystem, IAwake, IEndUpdate
    {
        public Socket listener;
        public SocketAsyncEventArgs receiveArgs;
        public ConcurrentQueue<Action> completedAction;

        public void Awake()
        {
            Subscribe<ListenTcp>(ListenTcp);
        }

        public void EndUpdate()
        {
            if (completedAction == null)
                return;

            while (completedAction.TryDequeue(out var action))
            {
                try
                {
                    action();
                }
                catch (Exception e)
                {
                    new CoreTip(TipLevel.Error, e.ToString()).Publish();
                }
            }
        }

        public void ListenTcp(ref ListenTcp eventData)
        {
            var endPoint = new IPEndPoint(IPAddress.Any, eventData.port);

            listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            listener.NoDelay = true;
            listener.Bind(endPoint);
            listener.Listen(1000);

            receiveArgs = new SocketAsyncEventArgs();
            receiveArgs.Completed += OnCompleted;

            completedAction = new ConcurrentQueue<Action>();

            AcceptAsync();

            new CoreTip($"TCP服务器已启动, port:{eventData.port}").Publish();
        }

        private void AcceptAsync()
        {
            var error = receiveArgs.SocketError;
            var accept = receiveArgs.AcceptSocket;
            receiveArgs.AcceptSocket = null;
            if (listener.AcceptAsync(receiveArgs))
                return;

            OnAcceptCompleted(error, accept);
        }

        private void OnAcceptCompleted(SocketError error, Socket socket)
        {
            new CoreTip(TipLevel.Log, $"接收连接, ip:{socket.RemoteEndPoint}").Publish();
            if (error == SocketError.Success)
                new AcceptTcpConnect(socket).Publish();

            AcceptAsync();
        }

        private void OnCompleted(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Accept:
                    completedAction.Enqueue(() => OnAcceptCompleted(e.SocketError, e.AcceptSocket));
                    break;
                default:
                    new CoreTip(TipLevel.Error, $"异常类型监听类型：{e.LastOperation}, {e.SocketError}").Publish();
                    break;
            }
        }
    }

    public struct AcceptTcpConnect : IEventData
    {
        public Socket acceptSocket;
        public AcceptTcpConnect(Socket acceptSocket)
        {
            this.acceptSocket = acceptSocket;
        }
    }

    public struct ListenTcp : IEventData
    {
        public int port;

        public ListenTcp(int port)
        {
            this.port = port;
        }
    }
}
